#include "klib.h"
#include "cte.h"
#include "proc.h"

#define PROC_NUM 64

static __attribute__((used)) int next_pid = 1;

proc_t pcb[PROC_NUM];
static proc_t *curr = &pcb[0];

void init_proc() {
  // Lab2-1, set status and pgdir
  pcb[0].status = RUNNING;
  pcb[0].pgdir = vm_curr();
  sem_init(&(pcb[0].zombie_sem), 0);
  // curr -> kstack = （(void*)(KER_MEM-PGSIZE)）;

  // Lab2-4, init zombie_sem
  // Lab3-2, set cwd
}

proc_t *proc_alloc() {
  // Lab2-1: find a unused pcb from pcb[1..PROC_NUM-1], return NULL if no such one
  for (int i = 1; i < PROC_NUM; i++) {
    if (pcb[i].status == UNUSED) {
      pcb[i].pid = next_pid;
      next_pid++;

      pcb[i].status = UNINIT;

      pcb[i].pgdir = vm_alloc();

      pcb[i].brk = 0;

      pcb[i].kstack = kalloc();

      pcb[i].ctx = &(pcb[i].kstack->ctx);

      pcb[i].parent = NULL;

      pcb[i].child_num = 0;

      sem_init(&(pcb[i].zombie_sem), 0);

      for (int j = 0; j < MAX_USEM; j++) {
        pcb[i].usems[j] = NULL;
      }
      return &pcb[i];
    }
  }
  return NULL;
  // init ALL attributes of the pcb
}

void proc_free(proc_t *proc) {
  // Lab2-1: free proc's pgdir and kstack and mark it UNUSED
  proc->status = UNUSED;
}

proc_t *proc_curr() {
  return curr;
}

void proc_run(proc_t *proc) {
  proc->status = RUNNING;
  curr = proc;
  set_cr3(proc->pgdir);
  set_tss(KSEL(SEG_KDATA), (uint32_t)STACK_TOP(proc->kstack));
  irq_iret(proc->ctx);
}

void proc_addready(proc_t *proc) {
  // Lab2-1: mark proc READY
  proc->status = READY;
}

void proc_yield() {
  // Lab2-1: mark curr proc READY, then int $0x81
  curr->status = READY;
  INT(0x81);
}

void proc_copycurr(proc_t *proc) {
  // Lab2-2: copy curr proc
  /*void vm_copycurr(PD *pgdir);这个函数位于kernel/src/vme.c，用途是复制当前的虚拟地址空间到pgdir这个页目录里。
调用时要求pgdir是刚vm_alloc出来的，只有[0, PHY_MEM)的恒等映射的页目录。*/

  PD* pgdir = vm_alloc();
  vm_copycurr(pgdir);
  proc->pgdir = pgdir;
  // vm_copycurr(proc->pgdir);

  proc_t* cur = proc_curr();

  proc->brk = cur->brk;

    /*
    *proc = *curr;
    proc->ctx = &(proc->kstack->ctx);
    proc->ctx->eax = 0;
  */

    proc->kstack->ctx = cur->kstack->ctx;
    proc->kstack->ctx.eax = 0;
    // proc->ctx->eax = 0;

    proc->parent = cur;
    cur->child_num++;
    // printf("这里！proc->pid: %d\n", proc->pid);
    // return proc;
  


  // Lab2-5: dup opened usems
  for (int i = 0; i < MAX_USEM; i++) {
    if (curr->usems[i] != NULL) {
      proc->usems[i] = usem_dup(curr->usems[i]);
      
      
      
      //不知道对不对
      /*if (proc->usems[i] != NULL) {
        sem_p(proc->usems[i]);
      }*/
    }
  }


  // Lab3-1: dup opened files
  // Lab3-2: dup cwd
}

void proc_makezombie(proc_t *proc, int exitcode) {
  // Lab2-3: mark proc ZOMBIE and record exitcode, set children's parent to NULL
  proc->status = ZOMBIE;
  proc->exit_code = exitcode;
  for (int i = 1; i < PROC_NUM; i++) {
    if (pcb[i].parent == proc) {
      pcb[i].parent = NULL;
    }
  }

  // Lab2-4: V(proc->zombie_sem)
  if (proc->parent != NULL) {
    sem_v(&(proc->parent->zombie_sem));
  }
 


  // Lab2-5: close opened usem
  for (int i = 0; i < MAX_USEM; i++) {
    if (proc->usems[i] != NULL) {
      usem_close(proc->usems[i]);
      proc->usems[i] = NULL;
    }
  }
  // Lab3-1: close opened files
  // Lab3-2: close cwd
 
}

proc_t *proc_findzombie(proc_t *proc) {
  // Lab2-3: find a ZOMBIE whose parent is proc, return NULL if none
  for (int i = 1; i < PROC_NUM; i++) {
    if (pcb[i].status == ZOMBIE && pcb[i].parent == proc) {
      return &pcb[i];
    }
  }
  return NULL;
}

void proc_block() {
  // Lab2-4: mark curr proc BLOCKED, then int $0x81
  curr->status = BLOCKED;
  INT(0x81);
}

int proc_allocusem(proc_t *proc) {
  // Lab2-5: find a free slot in proc->usems, return its index, or -1 if none
  for (int i = 0; i < MAX_USEM; i++) {
    if (proc->usems[i] == NULL) {
      return i;
    }
  }
  return -1;
}

usem_t *proc_getusem(proc_t *proc, int sem_id) {
  // Lab2-5: return proc->usems[sem_id], or NULL if sem_id out of bound
  if (sem_id < 0 || sem_id >= MAX_USEM) {
    return NULL;
  }
  return proc->usems[sem_id];
  
}

int proc_allocfile(proc_t *proc) {
  // Lab3-1: find a free slot in proc->files, return its index, or -1 if none
  TODO();
}

file_t *proc_getfile(proc_t *proc, int fd) {
  // Lab3-1: return proc->files[fd], or NULL if fd out of bound
  TODO();
}

void schedule(Context *ctx) {

  // Lab2-1: save ctx to curr->ctx, then find a READY proc and run it
  curr->ctx = ctx;
  for (int i = (curr - pcb + 1) % PROC_NUM; ; i = (i + 1) % PROC_NUM) {
    if (pcb[i].status == READY) {
      proc_run(&pcb[i]);
      irq_iret(pcb[i].ctx);

      break;
    }
  }



  
}
